
#include <linux/if_ether.h>
#include <linux/if_packet.h>
#include <netinet/if_ether.h>
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>

// int sk = socket(PF_PACKET, SOCK_RAW, htons(0x88CC));/*创建原始套接字*/
// https://www.xuebuyuan.com/1764043.html

// int socket(int domain, int type, int protocol);

static void recv_socket_dgram_protocol(const char* dev_name, unsigned short protocol, const char* dest_mac)
{
    u_int8_t buf[1024];
    int flag = 0;

    // PF_PACKET
    struct sockaddr_ll sll; // PF_PACKET地址结构体
    memset(&sll, 0, sizeof(sll));

    sll.sll_family = PF_PACKET; // 以太网包
    // /*sll虽然是用来指定目的地址的，但是在这个结构体中sll_ifindex 却指定的是本机发送报文的接口索引*/
    // sll.sll_ifindex = if_nametoindex("eth0");
    sll.sll_ifindex = if_nametoindex(dev_name);
    // sll.sll_protocol = htons(0x88CC);
    // sll.sll_protocol = htons(ETH_P_IP);
    // sll.sll_protocol = htons(ETH_P_ALL);
    sll.sll_protocol = htons(protocol);
    // sll.sll_addr[0] = 0x34; /*sll_addr指定目的MAC地址*/
    // sll.sll_addr[1] = 0xb0;
    // sll.sll_addr[2] = 0x52;
    // sll.sll_addr[3] = 0xda;
    // sll.sll_addr[4] = 0xda;
    // sll.sll_addr[5] = 0x18;
    // sscanf(dest_mac, "%02x:%02x:%02x:%02x:%02x:%02x", &sll.sll_addr[0], &sll.sll_addr[1], &sll.sll_addr[2], &sll.sll_addr[3], &sll.sll_addr[4], &sll.sll_addr[5]);
    socklen_t len = sizeof(sll);

    // int fd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
    // int fd = socket(PF_PACKET, SOCK_RAW, htons(0x80e0));
    // int fd = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_ALL));
    int fd = socket(PF_PACKET, SOCK_DGRAM, htons(protocol));
    // int fd = socket(PF_PACKET, SOCK_DGRAM, htons(0x80e0));

    perror("????");
    // sendto(sk, lpacket, 14, 0, (struct sockaddr*)&sll, sizeof(struct sockaddr_ll));
    int i = 0;
    while (fd >= 0) {
        int res = recvfrom(fd, buf, 100, flag, (struct sockaddr*)&sll, &len);
        if (buf[12] == 0x70 && buf[13] == 0xe0)
            i++;
        for (int j = 0; j < 12; j++)
            printf("%02x:", buf[j]);
        printf("type: %02x%02x, 0x70e0 count: %d\n", buf[12], buf[13], i);
        // int res = recvfrom(fd, buf, 100, flag, NULL, NULL);
        // printf("read data len: %d\n", res);
        // for (int i = 0; i < res; i++) {
        //     printf("%c", buf[i]);
        // }
    }
}

static void send_socket_dgram_protocol(const char* dev_name, unsigned short protocol, const char* dest_mac)
{
    u_int8_t buf[1024];
    int flag = 0;

    // PF_PACKET
    struct sockaddr_ll sll;
    memset(&sll, 0, sizeof(sll));

    sll.sll_family = PF_PACKET;
    // /*sll虽然是用来指定目的地址的，但是在这个结构体中sll_ifindex 却指定的是本机发送报文的接口索引*/
    // sll.sll_ifindex = if_nametoindex("eth0");
    // sll.sll_ifindex = if_nametoindex("enp0s20f0u2u2");
    sll.sll_ifindex = if_nametoindex(dev_name);
    // sll.sll_protocol = htons(0x88CC);
    // sll.sll_protocol = htons(ETH_P_IP);
    // sll.sll_protocol = htons(ETH_P_ALL);
    // sll.sll_protocol = htons(0x70e0);
    sll.sll_protocol = htons(protocol);
    // sll.sll_addr[0] = 0x34; /*sll_addr指定目的MAC地址*/
    // sll.sll_addr[1] = 0xb0;
    // sll.sll_addr[2] = 0x52;
    // sll.sll_addr[3] = 0xda;
    // sll.sll_addr[4] = 0xda;
    // sll.sll_addr[5] = 0x18;
    sscanf(dest_mac, "%02x:%02x:%02x:%02x:%02x:%02x", &sll.sll_addr[0], &sll.sll_addr[1], &sll.sll_addr[2], &sll.sll_addr[3], &sll.sll_addr[4], &sll.sll_addr[5]);

    socklen_t len = sizeof(sll);

    // int fd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
    // int fd = socket(PF_PACKET, SOCK_RAW, htons(protocol));
    // int fd = socket(PF_PACKET, SOCK_RAW, htons(0x80e0));
    // int fd = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_ALL));
    int fd = socket(PF_PACKET, SOCK_DGRAM, htons(protocol));
    // int fd = socket(PF_PACKET, SOCK_DGRAM, htons(0x80e0));

    perror("????");
    // sendto(sk, lpacket, 14, 0, (struct sockaddr*)&sll, sizeof(struct sockaddr_ll));
    int i = 0;
    memset(buf, 0, sizeof(buf));
    while (fd >= 0) {
        buf[12] = 0x70;
        buf[13] = 0xe0;
        buf[24] = i;
        int res = sendto(fd, buf, 100, flag, (struct sockaddr*)&sll, len);
        i++;
        // int res = recvfrom(fd, buf, 100, flag, (struct sockaddr*)&sll, &len);
        for (int j = 0; j < 12; j++)
            printf("%02x:", buf[j]);
        printf(" type: %02x%02x, count: %d\n", buf[12], buf[13], i);
        usleep(100000);
        // int res = recvfrom(fd, buf, 100, flag, NULL, NULL);
        // printf("read data len: %d\n", res);
        // for (int i = 0; i < res; i++) {
        //     printf("%c", buf[i]);
        // }
    }
}

static void test_socket_all(const char* dev_name, unsigned short protocol, const char* dest_mac)
{
    u_int8_t buf[1024];
    int flag = 0;

    // PF_PACKET
    struct sockaddr_ll sll;
    memset(&sll, 0, sizeof(sll));

    sll.sll_family = PF_PACKET;
    // /*sll虽然是用来指定目的地址的，但是在这个结构体中sll_ifindex 却指定的是本机发送报文的接口索引*/
    // sll.sll_ifindex = if_nametoindex("eth0");
    // sll.sll_ifindex = if_nametoindex("enp0s20f0u2u2");
    sll.sll_ifindex = if_nametoindex(dev_name);
    // sll.sll_protocol = htons(0x88CC);
    // sll.sll_protocol = htons(ETH_P_IP);
    // sll.sll_protocol = htons(ETH_P_ALL);
    sll.sll_protocol = htons(0x70e0);
    // sll.sll_addr[0] = 0x34; /*sll_addr指定目的MAC地址*/
    // sll.sll_addr[1] = 0xb0;
    // sll.sll_addr[2] = 0x52;
    // sll.sll_addr[3] = 0xda;
    // sll.sll_addr[4] = 0xda;
    // sll.sll_addr[5] = 0x18;
    socklen_t len = sizeof(sll);

    // int fd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
    int fd = socket(PF_PACKET, SOCK_RAW, htons(0x70e0));
    // int fd = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_ALL));
    // int fd = socket(PF_PACKET, SOCK_DGRAM, htons(0x80e0));

    perror("????");
    // sendto(sk, lpacket, 14, 0, (struct sockaddr*)&sll, sizeof(struct sockaddr_ll));
    int i = 0;

    while (fd >= 0) {
        int res = recvfrom(fd, buf, 100, flag, (struct sockaddr*)&sll, &len);
        if (buf[12] == 0x70 && buf[13] == 0xe0)
            i++;
        for (int j = 0; j < 12; j++)
            printf("%02x:", buf[j]);
        printf("type: %02x%02x, 0x70e0 count: %d\n", buf[12], buf[13], i);
        // int res = recvfrom(fd, buf, 100, flag, NULL, NULL);
        // printf("read data len: %d\n", res);
        // for (int i = 0; i < res; i++) {
        //     printf("%c", buf[i]);
        // }
    }
}

// int socket(int domain, int type, int protocol);
static void send_socket_all(const char* dev_name, unsigned short protocol, const char* dest_mac)
{
    u_int8_t buf[1024];
    int flag = 0;

    // PF_PACKET
    struct sockaddr_ll sll;
    memset(&sll, 0, sizeof(sll));

    sll.sll_family = PF_PACKET;
    // /*sll虽然是用来指定目的地址的，但是在这个结构体中sll_ifindex 却指定的是本机发送报文的接口索引*/
    // sll.sll_ifindex = if_nametoindex("eth0");
    // sll.sll_ifindex = if_nametoindex("enp0s20f0u2u2");
    sll.sll_ifindex = if_nametoindex(dev_name);
    // sll.sll_protocol = htons(0x88CC);
    // sll.sll_protocol = htons(ETH_P_IP);
    // sll.sll_protocol = htons(ETH_P_ALL);
    sll.sll_protocol = htons(0x70e0);
    // sll.sll_addr[0] = 0x34; /*sll_addr指定目的MAC地址*/
    // sll.sll_addr[1] = 0xb0;
    // sll.sll_addr[2] = 0x52;
    // sll.sll_addr[3] = 0xda;
    // sll.sll_addr[4] = 0xda;
    // sll.sll_addr[5] = 0x18;
    sscanf(dest_mac, "%02x:%02x:%02x:%02x:%02x:%02x", &sll.sll_addr[0], &sll.sll_addr[1], &sll.sll_addr[2], &sll.sll_addr[3], &sll.sll_addr[4], &sll.sll_addr[5]);
    socklen_t len = sizeof(sll);

    // int fd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
    int fd = socket(PF_PACKET, SOCK_RAW, htons(0x70e0));
    // int fd = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_ALL));
    // int fd = socket(PF_PACKET, SOCK_DGRAM, htons(0x80e0));

    perror("????");
    // sendto(sk, lpacket, 14, 0, (struct sockaddr*)&sll, sizeof(struct sockaddr_ll));
    int i = 0;
    memset(buf, 0, sizeof(buf));
    memcpy(buf, sll.sll_addr, 6);
    memset(buf, 0xff, 6);
    while (fd >= 0) {
        buf[12] = 0x70;
        buf[13] = 0xe0;
        // buf[12] = 0xe0;
        // buf[13] = 0x70;
        buf[24] = i;
        int res = sendto(fd, buf, 100, flag, (struct sockaddr*)&sll, len);
        i++;
        // int res = recvfrom(fd, buf, 100, flag, (struct sockaddr*)&sll, &len);
        for (int j = 0; j < 12; j++)
            printf("%02x:", buf[j]);
        printf(" type: %02x%02x, count: %d\n", buf[12], buf[13], i);
        usleep(100000);
        // int res = recvfrom(fd, buf, 100, flag, NULL, NULL);
        // printf("read data len: %d\n", res);
        // for (int i = 0; i < res; i++) {
        //     printf("%c", buf[i]);
        // }
    }
}

int main(int argc, char const* argv[])
{
    if (argc != 3) {
        printf("Usage: %s interface_name [c|s]\n", argv[0]);
        return 1;
    }
    const char* dest_mac = "00:0e:c6:57:e4:18";
    if (argv[2][0] == 'c')
        test_socket_all(argv[1], 0x70e0, dest_mac);
    // recv_socket_dgram_protocol(argv[1], 0x70e0, dest_mac);
    else if (argv[2][0] == 's')
        // send_socket_dgram_protocol(argv[1], 0x70e0, dest_mac);
        send_socket_all(argv[1], 0x70e0, dest_mac);
    else {
        printf("Usage: %s interface_name [c|s]\n", argv[0]);
        return 1;
    }

    return 0;
}
